/*
** Copyright (c) 2010 by Lorimark Solutions, LLC.
** All Rights Reserved.
** Send inquiries to: info@lorimarksolutions.com
**
*/

#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <evie-systems.h>
#include <font3x5.h>
#include <megaserial.h>
#include <serial.h>
#include <packet.h>
#include <crc8.h>
#include <touch.h>
#include "lcd.h"
#include "bargraph.h"

volatile uint8_t  moduleIndex;
volatile uint8_t  moduleNode;
volatile sint8    quadValue;
volatile sint8    pbValue;
volatile uint16_t totalPackCurrentVoltage;
volatile uint16_t totalPackMaxVoltage;
volatile uint16_t totalPackMinVoltage;
volatile uint16_t totalPackCurrentAmps;
volatile uint16_t totalPackMaxAmps;
volatile uint16_t totalPackMinAmps;
volatile uint16_t totalMotorCurrentAmps;
volatile uint16_t totalMotorMaxAmps;
volatile uint16_t totalMotorMinAmps;
volatile uint16_t auxBatteryCurrentTemp;
volatile uint16_t auxBatteryMaxTemp;
volatile uint16_t auxBatteryMinTemp;
volatile uint16_t auxBatteryCurrentVolts;
volatile uint16_t auxBatteryMaxVolts;
volatile uint16_t auxBatteryMinVolts;


const uint8_t   nodeName[8]    EEMEM = "LCD01";
const uint16_t  nodeSerial     EEMEM = 1000;
const uint8_t   nodeIDs[4]     EEMEM = { 250, 255, 255, 255 };
const uint8_t   display_screen EEMEM = 0;
const uint8_t   nodePad[17]    EEMEM = { 255, 255 };


/*
** define only *one* of the following:
**
*/
//#define BENCH_TEST
#define JOHNS_FIERO
//#define BATT_MOWER


#ifdef BATT_MOWER

#define TEMPERATURE_NODE 1

#define MODULE_COUNT 1
#define CELL_COUNT   4
#define VIN_LOW      8000
#define VIN_HIGH     16000

const mvc_t mvc[] =
{
// | module  | cell             | cell
// | address | count            | index
  {  1,        CELL_COUNT,        0         }, // 0
  {  0,        0,                 0         }, // 1
  {  0,        0,                 0         }, // 2
  {  0,        0,                 0         }, // 3
  {  0,        0,                 0         }, // 4
  {  0,        0,                 0         }, // 5
  {  0,        0,                 0         }, // 6
  {  0,        0,                 0         }, // 7
  {  0,        0,                 0         }
};
#endif


#ifdef BENCH_TEST

#define MODULE_COUNT 1
#define CELL_COUNT   32
#define VIN_LOW      8000
#define VIN_HIGH     16000

const mvc_t mvc[] =
{
// | module  | cell  | cell
// | address | count | index
  { 1,        8,       0         }, // 0
  { 0,        0,       0         }, // 1
  { 0,        0,       0         }, // 2
  { 0,        0,       0         }, // 3
  { 0,        0,       0         }, // 4
  { 0,        0,       0         }, // 5
  { 0,        0,       0         }, // 6
  { 0,        0,       0         }, // 7
  { 0,        0,       0         }
};
#endif


/*
** Pack Voltage Monitor for John's fiero
**
** John's Fiero has 8 of these modules, and
**  each module monitors 4 cells per.
**
*/
#ifdef JOHNS_FIERO

#define TEMPERATURE_NODE 6

#define MODULE_COUNT 4
#define CELL_COUNT   32
#define VIN_LOW      2200
#define VIN_HIGH     4200

const uint8  module_count EEMEM = MODULE_COUNT;
const uint8  cell_count   EEMEM = CELL_COUNT;
const uint16 vin_low      EEMEM = VIN_LOW;
const uint16 vin_high     EEMEM = VIN_HIGH;
const mvc_t mvc[12] EEMEM =
{
// | module  | cell  | cell
// | address | count | index
  { 1,        8,       0         }, // 0
  { 2,        8,       8         }, // 1
  { 3,        8,      16         }, // 2
  { 4,        8,      24         }, // 3
};
#endif




/*
** Traction Cell Voltage
**
*/
volatile uint16 tmt[12];
volatile tcv_t  tcv[100];

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
**  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
**
** USER INTERFACE PUSHBUTTONS
**
*/

#define UI_PB1 (1<<PC0)
#define UI_PB2 (1<<PC1)
#define UI_PB3 (1<<PB4)
#define UI_QU1 (1<<PC2)
#define UI_QU2 (1<<PC3)

#define UI_BUTTON1 (0x01)
#define UI_BUTTON2 (0x02)
#define UI_BUTTON3 (0x04)
#define UI_BUTTON4 (UI_BUTTON1 + UI_BUTTON2)
#define UI_BUTTON5 (UI_BUTTON1 + UI_BUTTON3)
#define UI_BUTTON6 (UI_BUTTON2 + UI_BUTTON3)

uint8 pb1(void)
{
  if( !(PINC & UI_PB1) ) return( UI_BUTTON1 );
  return( 0 );
}

uint8 pb2(void)
{
  if( !(PINC & UI_PB2) ) return( UI_BUTTON2 );
  return( 0 );
}

uint8 pb3(void)
{
  if( !(PINB & UI_PB3) ) return( UI_BUTTON4 );
  return( 0 );
}

uint8 pb4(void)
{
  return( pb1() + pb2() );
}

uint8 pb5(void)
{
  return( pb1() + pb3() );
}

uint8 pb6(void)
{
  return( pb2() + pb3() );
}

uint8 pball(void)
{
  return( pb1() + pb2() + pb3() );
}

uint8 qu1(void)
{
  if( !(PINC & UI_QU1) ) return( 0x10 );
  else                   return( 0x00 );
}

uint8 qu2(void)
{
  if( !(PINC & UI_QU2) ) return( 0x20 );
  else                   return( 0x00 );
}

uint8 quall(void)
{
  return( qu1() + qu2() );
}

void init_ui(void)
{
  /*
  ** Apply the pullups to the pins
  **
  */
  PORTC    |=
    UI_PB1 |
    UI_PB2 |
    UI_QU1 |
    UI_QU2 ;

  PORTB    |=
    UI_PB3 ;

  /*
  ** Make the DDR inputs for the buttons
  **
  */
  DDRC        &=
    ~(UI_PB1  |
      UI_PB2  |
      UI_QU1  |
      UI_QU2) ;

  DDRB        &=
    ~(UI_PB3) ;

  /*
  ** Set the quad value to nothing.
  **
  */
  quadValue  = 0;
  pbValue    = 0;

}


sint8 QuadTurns(void)
{
  sint8 retVal = quadValue;
  quadValue    = 0;
  return( retVal );
}

uint8 PbValue(void)
{
  uint8 retVal = pbValue;
  pbValue      = 0;
  return( retVal );
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
**  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
**
** 
**
*/

#define PIZ   (1<<PD7)

#define LED   (1<<PB3)

#define DB1   (1<<PC3)
#define DB2   (1<<PC2)
#define BLPWM (1<<PC4)

#define FLPDB1 PORTD ^= DB1
#define TOGDB1 FLPDB1; /* _delay_us(1); */ FLPDB1

#define FLPDB2 PORTD ^= DB2
#define TOGDB2 FLPDB2; /* _delay_us(1); */ FLPDB2

#define LED_FLASH_LONG  250
#define LED_FLASH_SHORT 15

uint16 ledFlash;
uint8  ledState;

void led_on(void)
{
  PORTB |=  LED;
}

void led_off(void)
{
  PORTB &= ~LED;
}

uint16 downCounter[4];

ISR(TIMER1_OVF_vect)
{
  static uint8 pbBounce   = 0;
  static uint8 lastPB     = 0;
  static uint8 lastQuad   = 0;

  uint8 thisPB   = pball();
  uint8 thisQuad = quall();
  
  if( lastQuad != thisQuad )
  {
    if( lastQuad == 0x00 )
    {
      if( thisQuad == 0x10 ) quadValue--;
      if( thisQuad == 0x20 ) quadValue++;
    }

    if( lastQuad == 0x10 )
    {
      if( thisQuad == 0x30 ) quadValue--;
      if( thisQuad == 0x00 ) quadValue++;
    }

    if( lastQuad == 0x20 )
    {
      if( thisQuad == 0x00 ) quadValue--;
      if( thisQuad == 0x30 ) quadValue++;
    }

    if( lastQuad == 0x30 )
    {
      if( thisQuad == 0x20 ) quadValue--;
      if( thisQuad == 0x10 ) quadValue++;
    }

    lastQuad = thisQuad;
  }


  if( thisPB != lastPB )
  {
    if( !pbBounce-- )
    {
      lastPB  = thisPB;
      pbValue = thisPB;
    }
  }
  else
  {
    pbBounce = 10;
  }

  if( !ledFlash-- )
  {
//    PORTD ^= LED;

    if( ledState )
    {
      led_on();
      ledFlash  = LED_FLASH_SHORT;
      ledState  = 0;
    }
    else
    {
      led_off();
      ledFlash  = LED_FLASH_LONG;
      ledState  = 1;
    }
  }

  if( downCounter[0] ) downCounter[0]--;
  if( downCounter[1] ) downCounter[1]--;
  if( downCounter[2] ) downCounter[2]--;
  if( downCounter[3] ) downCounter[3]--;

}

void init_timer(void)
{

  sei();

  TCCR1A = 
    (0b00   << COM1A0) | // 6.Compare Output Mode for A
    (0b00   << COM1B0) | // 4.Compare Output Mode for B 
    (0b00   << WGM10)  ; // 0.Waveform Generation Mode

  TCCR1B = 
    (0b0    << ICNC1)  | // 7.Input Capture Noise Canceler
    (0b0    << ICES1)  | // 6.Input Capture Edge Select
    (0b00   << WGM12)  | // 3.Waveform Generation Mode
    (0b001  << CS10)   ; // 0.Clock Select (1=no prescaling)

  TCCR1C =
    (0b0    << FOC1A)  | // 7.Force Output Compare for A
    (0b0    << FOC1B)  ; // 6.Force Output Compare for B

  TIMSK1 = 
    (0b0    << ICIE1)  | // 5.Input Capture Interrupt Enable
    (0b0    << OCIE1B) | // 2.Output Compare B Match Interrupt Enable
    (0b0    << OCIE1A) | // 1.Output Compare A Match Interrupt Enable
    (0b1    << TOIE1)  ; // 0.Overflow Interrupt Enable

//  OCR1AH = (20000>>8);
//  OCR1AL = (20000&255);

}


/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

uint8 NodeIDMatch(void)
{
  uint8 i;

  /*
  ** If the packet destination is the
  **  conditional broadcast address, then we
  **  see if our conditions are ready and 
  **  respond to that.
  **
  */
  if( packet.dst == 254 )
  {
    /*
    ** If the reed switch is low, then we respond
    **  to the conditional network address.
    **
    */
//    if( REED_IN ) 
//      return( 1 );

    /*
    ** If the touch network is shorted, then we
    **   respond to the conditional network address.
    **
    */
    if( touch_is_shorted() ) 
      return( 1 );

  } // endif( packet.dst == 254 )

  /*
  ** If the packet address matches one of our
  **  possible node addresses, then we respond
  **  to that also.
  **
  */
  for( i=0; i<4; i++ )
  {
    if( packet.dst == eeprom_read_byte(&nodeIDs[i]) )
    {
      return( 1 );
    }
  }

  /*
  ** If we cannot find a match above, then this
  **  packet was not addressed to us.
  **
  */
  return(0);
}



/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

void showBattery( uint8 bi )
{
//  uint8 i = bi;
//
//  bi &= 0b00000111;
//
//  battery = 
//    (packet.dat[(bi*2)+0])   +
//    (packet.dat[(bi*2)+1]*256);
//
//  if( batteries[i].current != battery || 
//      batteries[i].status  != 1          )
//  {
//    batteries[i].status  = 1;
//    batteries[i].last    = batteries[i].current;
//    batteries[i].current = battery;
//
////    if( (bi & 0b00000100) == 0b00000000 )
////      writeDecNumber_lcd( (20*i)+128, battery, 1000 );
//
////    if( (bi & 0b00000100) == 0b00000100 )
////      writeDecNumber_lcd( (20*i)+176, battery, 1000 );
//
//    if( battery > VOFFSET )
//    {
//      battery -= VOFFSET;
//      battery /= 40;
//    }
//    else
//    {
//      battery = 0;
//    }
//
//    drawBar( i, battery );
//
//  }
}

void showBatteries(void)
{
//  uint8 bi = (packet.src - 1) * CELL_COUNT; 
//  uint8 i;
//
//  for( i = 0; i < CELL_COUNT; i++ )
//  {
//    showBattery( bi + i );
//  }
//
}

void zapBatteries(void)
{
//  uint8 bi;
//
//  for( bi = 0; bi < CELL_COUNT; bi++ )
//  {
//    drawBar( bi, 0 );
//  }
}

/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

void ShowPackVoltage(void)
{
  uint8  i;





#ifdef NEVER
  totalPackMaxVoltage = 0;
  for( i = 0; i < CELL_COUNT; i++ )
  {
    totalPackMaxVoltage += tcv[i].max / 100;
  }

  writeHugeDecNumber_lcd( 46, totalPackMaxVoltage / 10, 100, -1 );
#endif






#ifdef NEVER
  totalPackMinVoltage = 0;
  for( i = 0; i < CELL_COUNT; i++ )
  {
    totalPackMinVoltage += tcv[i].min / 100;
  }

  writeBigDecNumber_lcd( 72, totalPackMinVoltage / 10, 100, -1 );
#endif





#ifndef NEVER

  writeHugeDecNumber_lcd( 76, totalPackCurrentAmps, 100, -1 );
  writeHugeCharacter_lcd( 'A' );

//    writeDecNumber_lcd( 70,     auxBatteryCurrentVolts, 10000 );
//    writeDecNumber_lcd( 100,    totalPackCurrentAmps,   10000 );
//    writeDecNumber_lcd( 70+128, totalMotorCurrentAmps,  10000 );

#endif


#ifdef NEVER
//  writeDecNumber_lcd( 64, tmt[0], 10000 );
  if( (tmt[TEMPERATURE_NODE-1] < 1500) && 
      (tmt[TEMPERATURE_NODE-1] > 0  )  )
  {
    writeHugeDecNumber_lcd( 69, tmt[TEMPERATURE_NODE-1], 1000, 3 );
    writeHugeCharacter_lcd( 'F' );
  }

#endif



#ifndef NEVER
  totalPackCurrentVoltage = 0;
  for( i = 0; i < eeprom_read_byte(&cell_count); i++ )
  {
    totalPackCurrentVoltage += tcv[i].current / 100;
  }

  writeHugeDecNumber_lcd( 0, totalPackCurrentVoltage, 1000, 3 );
  writeHugeCharacter_lcd( 'V' );
#endif



}


/*************************************************************************/
/*************************************************************************/
/*************************************************************************/


void InitScreenBarGraph(void)
{
  uint8 i;
  
  /*
  ** Get the LCD display formatted.  First print out all the
  **  numbers so that we know approximately what cell number
  **  we're looking at with a particular bar.  Then, print
  **  some little tick-marks so that we know what bar exactly
  **  the number is centered over.  Then print a dividing line
  **  so that we know where the top of the scale is.
  **
  **
  */
  writeDecNumber_lcd( 256 +  0,   1,  1 );
  writeDecNumber_lcd( 256 + 16,   5,  1 );
  writeDecNumber_lcd( 256 + 34,  10, 10 );
  writeDecNumber_lcd( 256 + 54,  15, 10 );
  writeDecNumber_lcd( 256 + 74,  20, 10 );
  writeDecNumber_lcd( 256 + 94,  25, 10 );
  writeDecNumber_lcd( 256 + 114, 30, 10 );

  pixel_lcd( 1,   0x16, 1 );
  pixel_lcd( 17,  0x16, 1 );
  pixel_lcd( 37,  0x16, 1 );
  pixel_lcd( 57,  0x16, 1 );
  pixel_lcd( 77,  0x16, 1 );
  pixel_lcd( 97,  0x16, 1 );
  pixel_lcd( 117, 0x16, 1 );

  for( i=0; i<128; i++ )
  {
    pixel_lcd( i, 0x17, 1 );
  }
}

void ShowScreenBarGraph(void)
{
  uint8  i;
  uint16 battery;
  uint16 min, max;

  ShowPackVoltage();

  for( i=0; i<eeprom_read_byte(&cell_count); i++ )
  {
    if( tcv[i].status == 1 )
    {
      battery = tcv[i].current;

      if( battery != tcv[i].last )
      {
        if( battery > eeprom_read_word(&vin_low) )
        {
          battery -= eeprom_read_word(&vin_low);
          battery /= (eeprom_read_word(&vin_high)-eeprom_read_word(&vin_low))/40;
        }
        else
        {
          battery = 0;
        }


        min = tcv[i].min;
        if( min > eeprom_read_word(&vin_low) )
        {
          min -= eeprom_read_word(&vin_low);
          min /= (eeprom_read_word(&vin_high)-eeprom_read_word(&vin_low))/40;
        }


        max = tcv[i].max;
        if( max > eeprom_read_word(&vin_low) )
        {
          max -= eeprom_read_word(&vin_low);
          max /= (eeprom_read_word(&vin_high)-eeprom_read_word(&vin_low))/40;
        }


        drawBar( i, battery, min, max );

        tcv[i].last = battery;

      } // battery != last

    } // status == 1

    /*
    ** Here we have a bad PVM or something, and our data is
    **  bad, so we should indicate that somehow on the display.
    **
    */
//    else // voltage status is bad
//    {
//      drawBar( i, 0 );
//    }

  } // for( all cells )

} // endvoid ShowBarGraph(void)

void EditScreenBarGraph(void)
{
}

/*************************************************************************/
/*************************************************************************/
/*************************************************************************/


void ShowRawAD(void)
{
  uint8  i;
  uint16 battery;

  for( i=0; i<eeprom_read_byte(&cell_count); i++ )
  {
    if( tcv[i].status == 1 )
    {
      battery = tcv[i].current;

//      if( battery != tcv[i].last )
      {
        writeDecNumberVert_lcd( i * 4 + 512, battery, 1000 );
      }

      tcv[i].last = battery;

    }

  }

}

/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

uint8 ModuleIndex( uint8 node )
{
  uint8 i;

  for( i=0; i<eeprom_read_byte(&module_count); i++ )
  {
    if( eeprom_read_byte(&mvc[i].node) == node )
    {
      return( i );
    }
  }

  return( -1 );
}

void ProcessRaw(void)
{
//  uint8 mi = ModuleIndex(packet.src);

//  if( mi == -1 ) return;

  /*
  ** Fetch the number of cells in this module
  **
  */
//  uint8 i = mvc[mi].cells;

}

void ProcessMVC(void)
{
  uint8 mi = ModuleIndex(packet.src);

  if( mi == -1 ) return;

  /*
  ** Fetch the number of cells in this module
  **
  */
  uint8 i = eeprom_read_byte(&mvc[mi].cells);

  /*
  ** Fetch the bar-graph cell index
  **
  */
  uint8 ci = eeprom_read_byte(&mvc[mi].tci) + i;

  /*
  ** Get the traction module temperature
  **
  */
  if( 21 == packet.cmd )
  {
    tmt[mi] = 
      (packet.dat[0])   +
      (packet.dat[1]*256);
  }

  /*
  ** Pull every cell from the packet array
  **  and stuff the values into the traction
  **  cell voltage array.
  **
  */
  while( i )
  {
    i--;
    ci--;

    if( 20 == packet.cmd )
    {
      tcv[ci].raw =
        (packet.dat[(i*2)+2])   +
        (packet.dat[(i*2)+3]*256);
    }
    
    if( 21 == packet.cmd )
    {
      tcv[ci].current =
        (packet.dat[(i*2)+2])   +
        (packet.dat[(i*2)+3]*256);
    }

    tcv[ci].status = 1;

  }

}


void ProcessMin(void)
{
  uint8 mi = ModuleIndex(packet.src);

  if( mi == -1 ) return;

  /*
  ** Fetch the number of cells in this module
  **
  */
  uint8 i = eeprom_read_byte(&mvc[mi].cells);

  /*
  ** Fetch the bar-graph cell index
  **
  */
  uint8 ci = eeprom_read_byte(&mvc[mi].tci) + i;

  /*
  ** Pull every cell from the packet array
  **  and stuff the values into the traction
  **  cell voltage array.
  **
  */
  while( i-- )
  {
    tcv[--ci].min =
      (packet.dat[(i*2)+2])   +
      (packet.dat[(i*2)+3]*256);
  }

}

void ProcessMax(void)
{
  uint8 mi = ModuleIndex(packet.src);

  if( mi == -1 ) return;

  /*
  ** Fetch the number of cells in this module
  **
  */
  uint8 i = eeprom_read_byte(&mvc[mi].cells);

  /*
  ** Fetch the bar-graph cell index
  **
  */
  uint8 ci = eeprom_read_byte(&mvc[mi].tci) + i;

  /*
  ** Pull every cell from the packet array
  **  and stuff the values into the traction
  **  cell voltage array.
  **
  */
  while( i-- )
  {
    tcv[--ci].max =
      (packet.dat[(i*2)+2])   +
      (packet.dat[(i*2)+3]*256);
  }

}


void ProcessAuxAmps(void)
{
  uint8 mi = ModuleIndex(packet.src);

  if( mi == -1 ) return;

  auxBatteryCurrentTemp  = 
    (packet.dat[0])      +
    (packet.dat[1]*256)  ;

  auxBatteryCurrentVolts =
    (packet.dat[2])      +
    (packet.dat[3]*256)  ;

  totalPackCurrentAmps   =
    (packet.dat[4])      +
    (packet.dat[5]*256)  ;

  totalMotorCurrentAmps  = 
    (packet.dat[6])      +
    (packet.dat[7]*256)  ;

}

void ProcessTest(void)
{
  uint8 mi = ModuleIndex(packet.src);

  if( mi == -1 ) return;

//  writeDecNumber_lcd( 128, packet.dat[0] + (packet.dat[1] * 256), 100 );
//  writeHexNumber_lcd( 128, packet.dat[0] + (packet.dat[1] * 256) );


}


uint8 ProcessNetworkResponse(void)
{
  if( !rxNetworkPacket() ) return( 0 );

  /* * * * * * * * * * * * * * * * * * * * * * *
  ** At this point we have a valid packet and it
  **  is addressed to us.  We can now process the
  **  data in it and do whatever is requested.
  **
  */

  /*
  ** Switch on the command
  **
  */
  switch( packet.cmd )
  {
    /*
    ** read out raw data
    **
    */
    case 20:
    {
      ProcessMVC();
      return(1);
    }

    /*
    ** read out scaled data
    **
    */
    case 21:
    {
      ProcessMVC();
      return(1);
    }

    /*
    ** read out average
    **
    */
    case 22:
    {
      ProcessMVC();
      return(1);
    }

    /*
    ** read out min
    **
    */
    case 23:
    {
      ProcessMin();
      return(1);
    }

    /*
    ** read out max
    **
    */
    case 24:
    {
      ProcessMax();
      return(1);
    }

    /*
    ** Read the aux/amps sensor.
    **
    */
    case 35:
    {
      ProcessAuxAmps();
      return(1);
    }

    case 250:
    {
      ProcessTest();
      return(1);
    }

  }

  return(0);

}



void DisconnectModule(void)
{
  /*
  ** Fetch the number of cells in this module
  **
  */
  uint8 i = eeprom_read_byte(&mvc[moduleIndex].cells);

  /*
  ** Fetch the bar-graph cell index
  **
  */
  uint8 ci = eeprom_read_byte(&mvc[moduleIndex].tci) + i;

  /*
  ** Set the status on each cell to 0
  **
  */
  while( i-- )
  {
    tcv[--ci].status = 0;
  }

}


uint8 WaitForAnswer(void)
{
  uint8 timeout = 9;

  while( !ProcessNetworkResponse() && --timeout )
  {
    _delay_ms(2);
  }

  if( !timeout )
  {
    DisconnectModule();
  }

  return( timeout );

}




/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

void txU16( uint8 channel, uint16 data )
{
  txPut( channel, data      );
  txPut( channel, data >> 8 );
}


/*
** Send a command to a node and wait for an answer.  If an answer doesn't
**  come, then retry.
**
*/
void SendCommand( uint8 targetNode, uint8 cmd, uint8 wait )
{
  uint8 retry = 3;

  while( retry-- )
  {
    crc8Result = 0;
    txPut( 0, ':'                             ); // atn
    txPut( 0, targetNode                      ); // dst
    txPut( 0, eeprom_read_byte(&nodeIDs[0])   ); // src
    txPut( 0, 0                               ); // status
    txPut( 0, cmd                             ); // cmd
    txPut( 0, 6                               ); // len
    txU16( 0, totalPackCurrentVoltage         ); // pack information
    txU16( 0, totalPackCurrentAmps            );
    txU16( 0, totalMotorCurrentAmps           );
    txPut( 0, crc8Result                      ); // crc

    if( !wait           ) return;
    if( WaitForAnswer() ) return;
  }

}


/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

void InitScreen0( void )
{
}

void ShowScreen0( void )
{
}

void EditScreen0( void )
{
}

/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

void InitScreenModuleCellVoltages( void )
{
  uint8 i;

  set_lcd_x( 0, 0 );
  puts_lcd( "nd:" );

  set_lcd_x( 1, 8 );
  puts_lcd( "ts:" );

  for( i=0; i<8; i++ )
  {
    set_lcd_x(  30, i*8  );
    putc_lcd(  '1'  + i  );
    putc_lcd(  ':'       );
  }
}

void ShowScreenModuleCellVoltages(void)
{
  uint8 i;
  static uint8 selected_module = 1;

  writeDecNumber_lcd( 12,  selected_module,          100 );
  writeDecNumber_lcd( 140, tmt[0]/100, 100 );
  writeDecNumber_lcd( 256, tmt[0], 10000 );

//  writeDecNumber_lcd( 256, tmt[selected_module], 10000 );


  for( i=0; i<8; i++ )
  {
    writeDecNumber_lcd( 36  + (128*i), tcv[i].raw,     1000  );
    writeDecNumber_lcd( 56  + (128*i), tcv[i].current, 10000 );
    writeDecNumber_lcd( 80  + (128*i), tcv[i].min,     10000 );
    writeDecNumber_lcd( 104 + (128*i), tcv[i].max,     10000 );
  }



}

void EditScreenModuleCellVoltages(void)
{
}

/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

#define SCREEN_COUNT 2

struct
{
  void (*initFunc)(void);
  void (*showFunc)(void);
  void (*editFunc)(void);
} screens[SCREEN_COUNT] = 
{
  { &InitScreenBarGraph,           &ShowScreenBarGraph,           &EditScreenBarGraph           },
  { &InitScreenModuleCellVoltages, &ShowScreenModuleCellVoltages, &EditScreenModuleCellVoltages },
};

/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

int main(void)
{
  uint8 pbv;
  uint8 node;

  /*
  ** Display Mode
  **
  ** This indicates what mode the display is in:
  **
  **  0 = ShowMode... this shows the current display
  **  1 = InitMode... this indicates that the display has changed and needs a refresh
  **  2 = EditMode... this indicates we are editing the current screen
  **
  */
  uint8 display_mode = 1;
  uint8 i;

  DDRD |= PIZ;
  DDRB |= LED;
  DDRC |= BLPWM;

  PORTC |= BLPWM;

  led_off();

  /*
  ** Initialize the world
  **
  */
  init_ui();
  init_timer();
  init_lcd(1);
  init_usart( 0, 10 );
  init_usart( 1, 32 );

  puts_lcd( "evie-systems LCD01-324p" );

  totalPackCurrentVoltage = 0;
  totalPackMaxVoltage     = 0;
  totalPackMinVoltage     = 0;
  totalPackCurrentAmps    = 0;
  totalPackMaxAmps        = 0;
  totalPackMaxAmps        = 0;
  totalMotorCurrentAmps   = 0;
  totalMotorMaxAmps       = 0;
  totalMotorMinAmps       = 0;

  /*
  ** Let'r rip!
  **
  */
  ENABLE_INTERRUPTS;

  _delay_ms(3000);

  
  /*
  ** Initialize all the batteries so that they produce
  **  some sort of activity on initialization.
  **
  */
  for( i=0; i<eeprom_read_byte(&cell_count); i++ )
    tcv[i].status = -1;

  /*
  ** Reset all the module min/max readings
  **
  */
  for( moduleIndex=0; moduleIndex<eeprom_read_byte(&module_count); moduleIndex++ )
  {
    node = eeprom_read_byte(&mvc[moduleIndex].node);
    SendCommand( node, 43, 0 );
    SendCommand( node, 44, 0 );
  }

  /*
  ** Main Control Loop
  **
  */
  forever
  {

    /*
    ** Read all the voltage modules.
    **
    */
    for( moduleIndex=0; moduleIndex<eeprom_read_byte(&module_count); moduleIndex++ )
    {
      node = eeprom_read_byte(&mvc[moduleIndex].node);
      SendCommand( node, 20, 1 ); // raw
      SendCommand( node, 21, 1 ); // scaled

      if( !downCounter[0] )
      {
        SendCommand( node, 23, 1 ); // min
        SendCommand( node, 24, 1 ); // max
      }
    }

    if( !downCounter[0] ) 
    {
      txPut( 1, 'a' );
      downCounter[0] = 1000;
    }

    /*
    ** If the display screen has changed, then clear
    **  the screen and call the initFunc() so as to
    **  initialize the new screen.
    **
    */
    if( display_mode == 1 )
    {
      fillScreen_lcd(0x00); // Clear the display
      (screens[eeprom_read_byte(&display_screen)].initFunc)();
      display_mode = 0;
    }

    /*
    ** Update the display
    **
    */
    (screens[eeprom_read_byte(&display_screen)].showFunc)();

    /*
    ** If a button gets pressed, we need to sequence through
    **  the display screens.
    **
    */
    pbv = PbValue();
    if( pbv )
    {
      i = eeprom_read_byte(&display_screen);

      display_mode = 1;


      switch( pbv )
      {
        /*
        ** The TOP button sequences backwards through the list
        **  of display screens.
        **
        */
        case UI_BUTTON1:
        {
          if( i == 0 ) 
              eeprom_write_byte(((uint8_t*)&display_screen),SCREEN_COUNT-1);
          else
              eeprom_write_byte(((uint8_t*)&display_screen),i-1);
          break;

        } // endcase BUTTON_TOP:

        /*
        ** The BOT button sequences forwards through the list
        **  of display screens.
        **
        */
        case UI_BUTTON2:
        {
          if( i == SCREEN_COUNT-1 ) 
              eeprom_write_byte(((uint8_t*)&display_screen),0);
          else
              eeprom_write_byte(((uint8_t*)&display_screen),i+1);
          break;

        } // endcase BUTTON_BOT:

      } // endswitch( pbv )

    } // endif( pbv )

  } // endforever

} // endint main(void)


